SSR 方案:vite-plugin-ssr 与 vite-ssr
在理解了 SSR 底层原理之后,本文介绍两个基于 Vite 的 SSR 解决方案:vite-ssr(轻量级)和 vite-plugin-ssr(功能完善)。它们代表了两种不同复杂度的 SSR 实现思路,适用于不同的项目场景。
SSR 的两大核心难题
手写 SSR 应用时,有两个核心问题需要解决:
| 难题 | 说明 |
|---|---|
| 静态资源管理 | 如何处理 JS Bundle、CSS 等资源的引用和加载 |
| 状态与数据管理 | 如何处理组件状态、组件间通信、接口级数据的获取与传递 |
成熟的 SSR 框架必须妥善解决这两个问题。
vite-ssr:轻量级方案
vite-ssr 是 Vite 官方生态中的轻量 SSR 插件,适合对 SSR 有基本需求的项目。
核心特点
- 集成简单:直接
import引入即可使用 - 支持 Vue 和 React
- 数据获取方式直接:通过判断环境变量决定在服务端还是客户端获取数据
数据获取的三种方式
方式一:路由守卫(beforeEach)
在路由跳转前获取数据,将状态挂载到页面组件上:
router.beforeEach(async (to) => {
const data = await fetchData(to.path)
// 将数据设置到页面状态中
})
javascript
方式二:Suspense 组件 + useFetch
利用 Vue 的 <Suspense> 实验特性和异步组件:
<template>
<Suspense>
<template #default>
<AsyncPage />
</template>
<template #fallback>
<Loading />
</template>
</Suspense>
</template>
vue
<Suspense> 的特性:当异步组件的数据未返回时,显示 fallback 槽位的内容(如 Loading),数据就绪后自动切换到默认内容。
方式三:onServerPrefetch 钩子
Vue 提供了 onServerPrefetch 生命周期钩子,仅在 SSR 场景下执行:
<script setup>
import { onServerPrefetch, ref } from 'vue'
const data = ref(null)
onServerPrefetch(async () => {
// 仅在服务端执行
data.value = await fetchAPIData()
})
</script>
vue
这个钩子类似于 Nuxt 中的
useAsyncData,可以在服务端渲染时获取数据并初始化页面状态。它位于 Vue 官方文档的 Options API 和 Composition API 生命周期部分。
vite-ssr 的定位
适合轻量化场景,集成方式比 vite-plugin-ssr 简化。基本用法:
import viteSSR from 'vite-ssr'
import App from './App.vue'
import routes from './routes'
viteSSR(App, { routes }, ({ app, router, isClient }) => {
// 初始化逻辑
})
javascript
vite-plugin-ssr:功能完善的方案
vite-plugin-ssr(现已更名为 Vike)是一个类似 Nuxt 的 Vite SSR 插件,核心理念是约定大于配置。
核心特点
- 支持 Vue、React,甚至 Vue 2
- 文件路由系统:基于文件目录结构自动生成路由
- 细粒度渲染控制:通过文件后缀区分服务端/客户端代码
- 提供中文文档
创新点一:文件后缀控制运行环境
vite-plugin-ssr 通过文件名后缀来控制代码的运行环境,这是最核心的设计理念:
| 文件后缀 | 运行环境 | 说明 |
|---|---|---|
*.page.vue | 服务端 + 客户端 | 两端都运行 |
*.page.client.js | 仅客户端 | 只在浏览器运行 |
*.page.server.js | 仅服务端 | 只在 Node.js 运行 |
通过文件名就能清晰区分代码的运行位置,无需手动编写环境判断逻辑。
创新点二:Renderer 模板
不必为每个页面创建 page.client.js 和 page.server.js,可以在 _default.page.client.js 和 _default.page.server.js 中编写通用模板:
renderer/
├── _default.page.client.js # 所有页面的客户端通用逻辑
├── _default.page.server.js # 所有页面的服务端通用逻辑
└── html.js # HTML 外壳模板
text
这类似于 Nuxt 中的 Layout 概念。
数据获取流程
vite-plugin-ssr 的数据获取基于文件约定:
pages/
├── movie.page.vue # 页面组件(两端运行)
├── movie.page.route.js # 路由定义
└── movie.page.server.js # 服务端数据获取
text
服务端数据获取文件(*.page.server.js):
// movie.page.server.js
import fetch from 'node-fetch'
// 定义需要传递给客户端的数据
export const passToClient = ['pageProps']
// 在渲染前获取数据
export async function onBeforeRender(pageContext) {
const { movieId } = pageContext.routeParams
const response = await fetch(`https://api.example.com/movies/${movieId}`)
const movie = await response.json()
return {
pageContext: {
pageProps: { movie },
},
}
}
javascript
数据流说明:
movie.page.server.js
→ onBeforeRender() 获取数据
→ passToClient 序列化数据
→ pageContext.pageProps 传递给客户端
→ 页面组件通过 props 接收数据
text
与 Nuxt 的对比
| 特性 | vite-plugin-ssr | Nuxt |
|---|---|---|
| 渲染控制 | 暴露底层细节,灵活度高 | 封装完善,上手更快 |
| 文件路由 | 支持 | 支持 |
| 数据获取 | onBeforeRender + passToClient | useAsyncData / useFetch |
| 生态 | 较小 | 丰富的模块系统 |
| 适用场景 | 需要细粒度控制的项目 | 大多数生产项目 |
如何选择
| 场景 | 推荐方案 |
|---|---|
| 快速上手,功能全面 | Nuxt.js |
| 轻量化 SSR 需求 | vite-ssr |
| 需要精细控制渲染流程 | vite-plugin-ssr (Vike) |
| 理解 SSR 原理 | 手写 SSR(参考上一节) |
生产环境建议:优先使用 Nuxt.js,其成熟的生态、丰富的模块和活跃的社区支持能大幅降低维护成本。vite-plugin-ssr 更适合需要高度自定义渲染流程的项目。
↑